package ws.very.util.stuct

import scala.language.dynamics
import scala.util.Try
import scala.collection.mutable.ArrayBuffer

protected trait DyMapLike[A] extends Dynamic {

  private def self: this.type = this
  def selectDynamic(field: S): A
  protected trait MyGet[T] extends DyMapLike[T] with Dynamic {

    val old: DyMapLike.this.type = self
    //def fieldIndexOption(field: S) = self.fieldIndexOption(field)

    protected def try2MyType(any: A): Try[T]
    // def applyOption(index: I = 0) = (self.applyOption(index).flatMap { a => try2MyType(a).toOption })
    def selectDynamic(field: S): T = (old.selectDynamic(field) match {
      case null => null.asInstanceOf[T]
      case other => try2MyType(other).get
    })
  }

  protected trait MyGet2[T] extends MyGet[T] {

    protected def any2MyType(any: A): T
    def try2MyType(any: A): Try[T] = {

      val t = Try { any2MyType(any) }
      if (t.isFailure) t.failed.foreach { _.printStackTrace() }
      t
    }

  }
  class option extends MyGet2[O[A]] {
    protected def any2MyType(any: A) = O(any)
    override def selectDynamic(field: S) = try2MyType(old.selectDynamic(field)).getOrElse(None)
  }

  def option = new option

  class int extends MyGet2[I] {
    protected def any2MyType(any: A) = any match {
      case n: Float => n.toInt
      case i: BigInt => i.toInt
      case s: S => s.toInt
      case i: I => i
    }
  }

  def int = new int

  class long extends MyGet2[L] {
    protected def any2MyType(any: A) = any match {
      case i: BigInt => i.toLong
      case s: S => s.toLong
      case i: I => i
      case i: L => i.toLong
    }
  }
  def long = new long

  class bigDec extends MyGet2[BigDecimal] {
    protected def any2MyType(any: A) = any match {
      case i: I => i
      case i: L => i
      case i: D => i
      case i: A => BigDecimal(i.toString)
    }
  }
  def bigDec = new bigDec

  class str extends MyGet2[S] {
    def any2MyType(any: A) = if (any == null) null else any.toString()
  }

  def str = new str

}
trait DyMap extends DyMapLike[A] /*with Map[S, A]*/ { self =>
  //def values: Itb[A]
  def toMap: Map[S, Any]
  def keySet: Set[S]

}
object DyMap {
  trait WithIndex {
    def apply(index: I = 0): A = applyOption(index).get
    protected def fieldIndex(field: S): I = fieldIndexOption(field).get
    def selectDynamic(field: S): A = apply(fieldIndex(field))
    protected def fieldIndexOption(field: S): O[I]
    protected def applyOption(index: I = 0): O[A]
  }
}

case class ResultRowImpl(colsIndex: S Map I, data: Seq[A]) extends DyMap with DyMap.WithIndex {
  def keySet: Set[S] = colsIndex.keySet
  assert(colsIndex.size == data.size)
  //  def fieldIndex(field: S) = colsIndex(field)
  //  def apply(i: I = 0) = data(i)
  lazy val toMap = colsIndex.map { case (n, i) => n -> this(i) }

  protected def fieldIndexOption(field: S): O[I] =
    colsIndex.get(field)

  protected def applyOption(index: I = 0): O[A] =
    Try { (data(index)) }.toOption
  override def toString() = toMap.toString()

  def values = data

  def select(fields: S*) = {
    val news = fields.map { f => toMap(f) }
    ResultRowImpl(fields.zipWithIndex.toMap, news.toSeq)
  }
  /*
  def +[T](kv: (S, A))= {
    val (k, v) = kv
    val i = fieldIndexOption(k).getOrElse(colsIndex.values.max + 1)

    val newData = (new ArrayBuffer() ++= (data))
    newData(i) = v
    ResultRowImpl(colsIndex + (k -> i), newData)
  }
  def -(key: S): this.type = ???
  def get(key: ws.very.util.stuct.S): A = ???
  def iterator: Iterator[(S, A)] = ???*/
}
case class DyMapImpl(map: Map[S, A]) extends DyMap {
  def selectDynamic(field: S): A = map.get(field).orNull
  def keySet = map.keySet
  def toMap = map
  def select(fields: S*) = {
    val news = fields.map { f => f -> toMap(f) }.toMap
    DyMapImpl(news)
  }
}



